﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.DataAccessLayer.ESGRatings;
using Sustainalytics.ClientDW.DataAccessLayer.ESGRatings.Fakes;
using Sustainalytics.ClientDW.DataAccessLayer.Fakes;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESGRatings.BusinessLogic;

namespace Sustainalytics.ESGRatings.Service.Tests
{
    [TestClass]
    public class DataCollectorTests
    {
        private static StubIClientDwRepository _corporateDataRepository;
        private static StubIWeightMatricesRepository _wmFakeRepo;
        private static Guid _companyId;

        private static ReportLine CorporateDataToReportLine(CorporateData companyData, IEnumerable<string> indicatorList)
        {
            return new ReportLine
            {
                CapitalIQID = companyData.CapitalIQID,
                CompanyName = companyData.CompanyName,
                PeerGroupName = companyData.PeerGroup,
                IndustryGroupName = companyData.SustainalyticsIndustryGroup,
                TemplateType = companyData.TemplateType,
                CompanyType = companyData.CompanyType,
                IndicatorsValue = indicatorList
            };
        }

        private static Dictionary<Guid, List<CompanyWeights>> GetCompanyWeightsDictionary(Guid fakeWeightMatrixId,
            List<CompanyWeights> companyWeights)
        {
            var dictionary = new Dictionary<Guid, List<CompanyWeights>> {{fakeWeightMatrixId, companyWeights}};
            return dictionary;
        }

        private static void SetupFakeWeightMatrixRepository(WeightMatrix fakeWeightMatrix,
               List<CompanyWeights> companyWeights)
        {
            _wmFakeRepo = new StubIWeightMatricesRepository
            {
                GetWeightMatrixGuid = guid => fakeWeightMatrix,
                ListCompanyWeightsIEnumerableOfGuid = guid => GetCompanyWeightsDictionary(fakeWeightMatrix.Id, companyWeights)
            };
        }

        private static void SetCompanyId()
        {
             _companyId = new Guid("11c43ee8-b9d3-4e51-b73f-bd9dda66e29c");
        }

        private static List<string> SetTwoFakeIndicatorNumbers()
        {
            var fakeIndicatorNumbers = new List<string> { "1.1", "1.2" };
            return fakeIndicatorNumbers;
        }

        private static WeightMatrix SetupFakeWeightMatrix(List<string> fakeIndicatorNumbers)
        {
            var fakeWeightMatrix = new WeightMatrix { Id = Guid.Empty, IndicatorsNumbers = fakeIndicatorNumbers };
            SetCompanyWeights(fakeIndicatorNumbers,fakeWeightMatrix);
            return fakeWeightMatrix;
        }

        private static WeightMatrix SetupFakeWeightMatrix(List<string> fakeIndicatorNumbers,IEnumerable<string> fakeCompanyWeights )
        {
            var fakeWeightMatrix = new WeightMatrix { Id = Guid.Empty, IndicatorsNumbers = fakeIndicatorNumbers };
            SetCompanyWeights(fakeCompanyWeights, fakeWeightMatrix);
            return fakeWeightMatrix;
        }

        private static void SetCompanyWeights(IEnumerable<string> fakeCompanyWeights,WeightMatrix fakeWeightMatrix)
        {
             var companyWeights = new List<CompanyWeights>
             {
                new CompanyWeights
                {
                    CompanyId = _companyId,
                    Weights = fakeCompanyWeights.ToDictionary(x => x, x => 0f)
                }
            };
            SetupFakeWeightMatrixRepository(fakeWeightMatrix, companyWeights);
        }

        private static TestESGRatingInDto SetESGCheckReportOptions(Guid? guid=null)
        {
            var options = new TestESGRatingInDto { CompanyId = _companyId, FileName = String.Empty, SelectedMatrixId = guid.HasValue? guid.Value:Guid.Empty};
            return options;
        }

        private static void SetCorporateDataRepository(List<CorporateData> corporateDataList)
        {

            _corporateDataRepository = new StubIClientDwRepository
            { 
            ListCompaniesCorporateDataIEnumerableOfGuid = guids => corporateDataList
            };
        }

        private static CorporateData SetCorporateData()
        {
            var corporateData = new CorporateData
            {
                Id = _companyId,
                CapitalIQID = "1",
                CompanyName = "Name",
                PeerGroup = "PeerGroup",
                SustainalyticsIndustryGroup = "IndustryGroup",
                CompanyType = "Type_A",
                TemplateType = "Public"
            };
            return corporateData;
        }

        [TestClass]
        public class CollectRawScoreTests
        {
           

           
        }


        [TestClass]
        public class DataCollectorWeightTests
        {

            [TestMethod]
            public void Collect_Weights_Null_WeightMatrix()
            {
                SetCompanyId();

                var options = SetESGCheckReportOptions();

                SetCorporateData();

               SetupFakeWeightMatrixRepository(null,new List<CompanyWeights>());

                SetCorporateDataRepository(new List<CorporateData>());

                var expectedReportLines = Enumerable.Empty<ReportLine>();

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>() });

                var reportLines = sutDataCollector.CollectWeights();

                Assert.IsTrue(expectedReportLines.SequenceEqual(reportLines));

            }

            [TestMethod]
            public void Collect_Weights_Empty_WeightMatrix()
            {
                 SetCompanyId();

                var options = SetESGCheckReportOptions();


                var fakeIndicatorNumbers = new List<string>();


                var companyWeights = new List<CompanyWeights> { new CompanyWeights { CompanyId = _companyId, Weights = fakeIndicatorNumbers.ToDictionary(x => x, x => 0f) } };

                var fakeWeightMatrix = new WeightMatrix { Id = Guid.Empty, IndicatorsNumbers = fakeIndicatorNumbers };

                SetupFakeWeightMatrixRepository(fakeWeightMatrix, companyWeights);

                SetCorporateDataRepository(new List<CorporateData>());

                IEnumerable<ReportLine> expectedReportLines = Enumerable.Empty<ReportLine>();

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                IEnumerable<ReportLine> reportLines = sutDataCollector.CollectWeights();

                Assert.IsTrue(expectedReportLines.SequenceEqual(reportLines));
            }

            [TestMethod]
            public void Collect_Weights_OrderedDataByPeerAndCompanyName_Test()
            {
                //arrange


                SetCompanyId();
                var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();
                var fakeWeightMatrix = SetupFakeWeightMatrix(fakeIndicatorNumbers);

                var corporateData1 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "B",
                    PeerGroup = "PeerGroup_A",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };
                var corporateData2 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "A",
                    PeerGroup = "PeerGroup_A",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };
                var corporateData3 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "A",
                    PeerGroup = "PeerGroup_B",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };

                SetCorporateDataRepository(new List<CorporateData> {corporateData1,corporateData2,corporateData3});

                var expectedReportLines = new[]
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(corporateData2, new List<string> {"0.00", "0.00"}),
                    CorporateDataToReportLine(corporateData1, new List<string> {"0.00", "0.00"}),
                    CorporateDataToReportLine(corporateData3, new List<string> {"0.00", "0.00"})
                };

                var options = SetESGCheckReportOptions();

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }


            [TestMethod]
            public void Collect_Weights_OrderedDataByPeerCompanyName_Test()
            {
                //arrange

              

                 SetCompanyId();
                 var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();
                 var fakeWeightMatrix = SetupFakeWeightMatrix(fakeIndicatorNumbers);

            
                var corporateData1 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "B",
                    PeerGroup = "PeerGroup",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };
                var corporateData2 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "A",
                    PeerGroup = "PeerGroup",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };

                SetCorporateDataRepository(new List<CorporateData> { corporateData1, corporateData2 });

                var expectedReportLines = new[]
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(corporateData2, new List<string> {"0.00", "0.00"}),
                    CorporateDataToReportLine(corporateData1, new List<string> {"0.00", "0.00"})
                };

                var options = SetESGCheckReportOptions();
                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                  new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_OrderedDataByPeerGroup_Test()
            {
                //arrange


                SetCompanyId();
                var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();
                var fakeWeightMatrix = SetupFakeWeightMatrix(fakeIndicatorNumbers);

                var corporateData1 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "Name",
                    PeerGroup = "B",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };
                var corporateData2 = new CorporateData
                {
                    Id = _companyId,
                    CapitalIQID = "1",
                    CompanyName = "Name",
                    PeerGroup = "A",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };

                SetCorporateDataRepository(new List<CorporateData> { corporateData1, corporateData2 });

                var expectedReportLines = new[]
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(corporateData2, new List<string> {"0.00", "0.00"}),
                    CorporateDataToReportLine(corporateData1, new List<string> {"0.00", "0.00"})
                };

                var options = SetESGCheckReportOptions();
                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_With_NA_Test()
            {
                //arrange

                var fakeWeightIndicatorNumbers = new List<string> { "1.1", "1.2", "1.3" };
                var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();

                SetCompanyId();

                var fakeWeightMatrix=SetupFakeWeightMatrix(fakeWeightIndicatorNumbers,fakeIndicatorNumbers);

                var corporateData = SetCorporateData();

                SetCorporateDataRepository(new List<CorporateData> { corporateData });

                var expectedReportLines = new[]
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(corporateData, new List<string> {"0.00", "0.00", "N/A"})
                };

                var options = SetESGCheckReportOptions();

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_Without_CompanyId()
            {
                //arrange

                var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers(); 

                var companyId1 = new Guid("11c43ee8-b9d3-4e51-b73f-bd9dda66e29c");
                var companyId2 = new Guid("11c43ee8-b9d3-4e51-b73f-bd9dda66e29d");

                var fakeWeightMatrix = new WeightMatrix { Id = Guid.Empty, IndicatorsNumbers = fakeIndicatorNumbers };
                var companyWeights = new List<CompanyWeights>
                {
                    new CompanyWeights
                    {
                        CompanyId = companyId1,
                        Weights = fakeIndicatorNumbers.ToDictionary(x => x, x => 0f)
                    },
                    new CompanyWeights
                    {
                        CompanyId = companyId2,
                        Weights = fakeIndicatorNumbers.ToDictionary(x => x, x => 0f)
                    }
                };

                SetupFakeWeightMatrixRepository(fakeWeightMatrix, companyWeights);

                var corporateData1 = new CorporateData
                {
                    Id = companyId1,
                    CapitalIQID = "1",
                    CompanyName = "B",
                    PeerGroup = "PeerGroup",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };
                var corporateData2 = new CorporateData
                {
                    Id = companyId2,
                    CapitalIQID = "2",
                    CompanyName = "A",
                    PeerGroup = "PeerGroup",
                    SustainalyticsIndustryGroup = "IndustryGroup",
                    CompanyType = "Type_A",
                    TemplateType = "Public"
                };

                SetCorporateDataRepository( new List<CorporateData> { corporateData1, corporateData2 });
               
                var expectedReportLines = new[]
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(corporateData2, new List<string> {"0.00", "0.00"}),
                    CorporateDataToReportLine(corporateData1, new List<string> {"0.00", "0.00"})
                };

                var options = new TestESGRatingInDto { CompanyId = Guid.Empty, FileName = String.Empty };
                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                  new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});
                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_Without_NA_Test()
            {
                //arrange

                

                 SetCompanyId();

                 var fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();
                 var fakeWeightMatrix = SetupFakeWeightMatrix(fakeIndicatorNumbers);

                var companyData = SetCorporateData();

                SetCorporateDataRepository(new List<CorporateData> { companyData });

                var expectedReportLines = new List<ReportLine>
                {
                    ReportLine.Header(fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(companyData, new List<string> {"0.00", "0.00"})
                };

                var options = SetESGCheckReportOptions();

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, options,
                   new ESGCompaniesScores { Scores = new List<ESGCompanyScores>()});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectWeights().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }
        }

        [TestClass]
        public class DataCollectorRatingsTests
        {
            
            private TestESGRatingInDto _options;
            private WeightMatrix _fakeWeightMatrix;
            private CorporateData _corporateData;
            private List<string> _fakeIndicatorNumbers;

            [TestInitialize]
            public void Setup()
            {

                SetCompanyId();

                 _fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();

                _fakeWeightMatrix = SetupFakeWeightMatrix(_fakeIndicatorNumbers);

               _corporateData = SetCorporateData();

                SetCorporateDataRepository(new List<CorporateData> { _corporateData });
                _options = SetESGCheckReportOptions(_fakeWeightMatrix.Id);
            }

            [TestMethod]
            public void Collect_Weights_With_NA_Test()
            {
                //arrange

             

                var expectedReportLines = new[]
                {
                    ReportLine.Header(_fakeWeightMatrix.IndicatorsNumbers),
                    CorporateDataToReportLine(_corporateData, new List<string> {"0.00",  "N/A"})
                };


                var esgCompanyScores = new ESGCompanyScores
                {
                    CompanyId = _companyId,
                    IndicatorScores = new float?[] { 0f, null },
                    AggregatorScores = new float?[] { 0f, null }
                };
                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, _options,
                    new ESGCompaniesScores { Scores = new List<ESGCompanyScores> { esgCompanyScores } });

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectRatings().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_Without_NA_Test()
            {
                //arrange
              
                var expectedReportLines = new List<ReportLine>
                {
                    ReportLine.Header(_fakeWeightMatrix.IndicatorsNumbers)
                    ,
                    CorporateDataToReportLine(_corporateData, new List<string> {"0.00", "0.00"})
                };
                var esgCompanyScores = new ESGCompanyScores
                {
                    CompanyId = _companyId,
                    IndicatorScores = new float?[] {0f, 0f},
                    AggregatorScores = new float?[] {0f, 0f}
                };
              


                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, _options,
                    new ESGCompaniesScores {Scores = new List<ESGCompanyScores> {esgCompanyScores}});

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectRatings().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestCleanup]
            public void Tear()
            {
                _wmFakeRepo = null;
                _corporateDataRepository = null;
            }
        }

        [TestClass]
        public class DataCollectorScoresTests
        {

            private TestESGRatingInDto _options;
            private WeightMatrix _fakeWeightMatrix;
            private CorporateData _corporateData;
            private List<string> _fakeIndicatorNumbers;

            [TestInitialize]
            public void Setup()
            {

                SetCompanyId();

                _fakeIndicatorNumbers = SetTwoFakeIndicatorNumbers();

                _fakeWeightMatrix = SetupFakeWeightMatrix(_fakeIndicatorNumbers);

                _corporateData = SetCorporateData();

                SetCorporateDataRepository(new List<CorporateData> { _corporateData });
                _options = SetESGCheckReportOptions(_fakeWeightMatrix.Id);
            }

            [TestMethod]
            public void Collect_Scores_With_NA_Test()
            {
                //arrange
                var expectedScores = Enumerable.Repeat("0.00", ReportLine.HeaderScores().IndicatorsValue.Count() - 1).ToList();
                expectedScores.Add("N/A");

                var expectedReportLines = new[]
                {
                    ReportLine.HeaderScores(),
                    CorporateDataToReportLine(_corporateData,expectedScores )
                };

                var scores =
                    Enumerable.Repeat((float?) 0f, ReportLine.HeaderScores().IndicatorsValue.Count() - 1).ToList();
                scores.Add(null);
                var esgCompanyScores = new ESGCompanyScores
                {
                    CompanyId = _companyId,
                    IndicatorScores = scores.ToArray(),
                    AggregatorScores = scores.ToArray()
                };

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, _options,
                    new ESGCompaniesScores { Scores = new List<ESGCompanyScores> { esgCompanyScores } });

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectScores().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestMethod]
            public void Collect_Weights_Without_NA_Test()
            {
                //arrange
                var expectedScores = Enumerable.Repeat("0.00", ReportLine.HeaderScores().IndicatorsValue.Count()).ToList();

                var expectedReportLines = new[]
                {
                    ReportLine.HeaderScores(),
                    CorporateDataToReportLine(_corporateData,expectedScores )
                };

                var scores =
                    Enumerable.Repeat((float?)0f, ReportLine.HeaderScores().IndicatorsValue.Count()).ToArray();

                var esgCompanyScores = new ESGCompanyScores
                {
                    CompanyId = _companyId,
                    IndicatorScores =scores.ToArray(),
                    AggregatorScores = scores.ToArray()
                };

                var sutDataCollector = new DataCollectorTestHelper(_wmFakeRepo, _corporateDataRepository, _options,
                    new ESGCompaniesScores { Scores = new List<ESGCompanyScores> { esgCompanyScores } });

                //act

                List<ReportLine> reportLines = sutDataCollector.CollectScores().ToList();

                //assert

                CollectionAssert.AreEqual(expectedReportLines, reportLines);
            }

            [TestCleanup]
            public void Tear()
            {
                _wmFakeRepo = null;
                _corporateDataRepository = null;
            }
        }

        private class DataCollectorTestHelper : DataCollector
        {
            private readonly ESGCompaniesScores _fakeScores;

            public DataCollectorTestHelper(IWeightMatricesRepository weightMatrixRepository,
                IClientDwRepository corporateDataRepository,
                TestESGRatingInDto options, ESGCompaniesScores fakeScores)
                : base(options, null, weightMatrixRepository, corporateDataRepository, null)
            {
                _fakeScores = fakeScores;
            }

            protected override string GetResponseFromScoresService()
            {
                return JsonConvert.SerializeObject(_fakeScores);
            }
        }
    }
}